import subprocess
import configparser
import os
import logging
import argparse
import mysql.connector  # 添加mysql连接器
import sys  # 添加sys模块用于退出程序
import shutil  # 添加shutil模块用于检查命令是否存在

# 获取脚本所在目录的绝对路径
SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
# 获取项目根目录（假设脚本在backend/tools目录下）
PROJECT_ROOT = os.path.abspath(os.path.join(SCRIPT_DIR, '..', '..'))
# 获取backend目录
BACKEND_ROOT = os.path.abspath(os.path.join(SCRIPT_DIR, '..'))

# 确保日志文件目录存在，如果不存在则创建
log_dir = os.path.join(PROJECT_ROOT, 'artifacts')
if not os.path.exists(log_dir):
    os.makedirs(log_dir)

# 创建日志记录器
logger = logging.getLogger()
logger.setLevel(logging.INFO)

# 创建一个日志文件 handler
log_file_path = os.path.join(log_dir, 'execution_log.log')
file_handler = logging.FileHandler(log_file_path)
file_handler.setLevel(logging.INFO)
formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s')
file_handler.setFormatter(formatter)

# 创建一个控制台日志 handler
console_handler = logging.StreamHandler()
console_handler.setLevel(logging.INFO)
console_handler.setFormatter(formatter)

# 将日志同时输出到文件和控制台
logger.addHandler(file_handler)
logger.addHandler(console_handler)

def check_mysql_client_installed():
    """
    检查是否安装了MySQL客户端工具
    """
    # 检查mysql命令行工具
    if shutil.which('mysql') is None:
        logger.error("错误: 未找到MySQL客户端工具。请确保MySQL客户端已安装并添加到PATH环境变量中。")
        sys.exit(1)

    try:
        # 检查MySQL版本
        result = subprocess.run(['mysql', '--version'],
                               stdout=subprocess.PIPE,
                               stderr=subprocess.PIPE,
                               universal_newlines=True,
                               check=True)
        logger.info(f"MySQL客户端版本信息: {result.stdout.strip()}")
    except subprocess.CalledProcessError:
        logger.error("错误: MySQL客户端安装可能存在问题，无法获取版本信息。")
        sys.exit(1)
    except Exception as e:
        logger.error(f"检查MySQL客户端时发生错误: {str(e)}")
        sys.exit(1)

def truncate_path(path, base_dir=None):
    """
    截断文件路径，使日志输出更简洁
    如果提供了base_dir，则只显示相对于base_dir的路径
    否则只显示最后3个目录部分
    """
    if base_dir and path.startswith(base_dir):
        # 如果路径以base_dir开头，则只显示相对路径部分
        rel_path = os.path.relpath(path, base_dir)
        return ".../" + rel_path
    else:
        # 否则只保留最后3个目录部分
        parts = path.split(os.sep)
        if len(parts) > 3:
            return ".../" + os.path.join(*parts[-3:])
        return path

def test_mysql_connection(host, user, password):
    """测试MySQL连接是否正常"""
    try:
        connection = mysql.connector.connect(
            host=host,
            user=user,
            password=password
        )
        connection.close()
        logger.info("MySQL连接测试成功！")
        return True
    except mysql.connector.Error as err:
        if err.errno == mysql.connector.errorcode.ER_ACCESS_DENIED_ERROR:
            logger.error("MySQL连接失败：用户名或密码错误")
        elif err.errno == mysql.connector.errorcode.CR_CONN_HOST_ERROR:
            logger.error("MySQL连接失败：无法连接到数据库服务器，请检查服务器是否运行或网络连接")
        else:
            logger.error("MySQL连接失败：{}".format(err))
        return False

def execute_sql_scripts_with_source(sql_file_paths, host, user, password, database):
    if not sql_file_paths:
        logger.warning("没有找到任何SQL文件！")
        return

    logger.info("待执行的 SQL 文件列表:")
    for file in sql_file_paths:
        logger.info(truncate_path(file, PROJECT_ROOT))

    for sql_file_path in sql_file_paths:
        if not os.path.exists(sql_file_path):
            logger.error("SQL文件不存在: {}".format(truncate_path(sql_file_path, PROJECT_ROOT)))
            continue

        logger.info("执行脚本: {}".format(truncate_path(sql_file_path, PROJECT_ROOT)))
        command = [
            'mysql',
            '-h', host,
            '-u', user,
            '--password={}'.format(password),
            database,
            '-e', "source {}".format(sql_file_path)
        ]

        try:
            # 兼容Python 3.6.8，不使用capture_output参数
            process = subprocess.Popen(
                command,
                stdout=subprocess.PIPE,
                stderr=subprocess.PIPE,
                universal_newlines=True  # 在Python 3.6中使用universal_newlines=True代替text=True
            )
            stdout, stderr = process.communicate()
            returncode = process.returncode

            # 检查命令执行状态
            if returncode != 0:
                logger.error("执行脚本 {} 失败".format(truncate_path(sql_file_path, PROJECT_ROOT)))
                logger.error("错误信息: {}".format(stderr))
                # 如果是严重错误，终止执行
                if "Access denied" in stderr or "Unknown database" in stderr:
                    logger.error("遇到严重错误，终止执行")
                    sys.exit(1)
            else:
                # 检查是否有警告信息
                if stderr and "password on the command line interface" not in stderr:
                    logger.warning("执行警告: {}".format(stderr))
                logger.info("脚本 {} 执行成功！".format(truncate_path(sql_file_path, PROJECT_ROOT)))

        except subprocess.CalledProcessError as e:
            logger.error("执行脚本 {} 时发生错误: {}".format(truncate_path(sql_file_path, PROJECT_ROOT), str(e)))
            logger.error("错误详情: {}".format(e.stderr if hasattr(e, 'stderr') else '未知错误'))
            sys.exit(1)
        except Exception as e:
            logger.error("执行脚本 {} 时发生未知错误: {}".format(truncate_path(sql_file_path, PROJECT_ROOT), str(e)))
            sys.exit(1)

def find_config_file(config_file_name):
    """查找配置文件，支持在不同目录下执行脚本"""
    # 尝试在当前目录查找
    if os.path.exists(config_file_name):
        return os.path.abspath(config_file_name)

    # 尝试在脚本所在目录查找
    script_dir_config = os.path.join(SCRIPT_DIR, config_file_name)
    if os.path.exists(script_dir_config):
        return script_dir_config

    # 尝试在backend目录查找
    backend_config = os.path.join(BACKEND_ROOT, config_file_name)
    if os.path.exists(backend_config):
        return backend_config

    # 尝试在项目根目录查找
    project_config = os.path.join(PROJECT_ROOT, config_file_name)
    if os.path.exists(project_config):
        return project_config

    # 如果都找不到，返回None
    return None

def read_config(config_file, config_env=None):
    try:
        # 查找配置文件
        config_path = find_config_file(config_file)
        if not config_path:
            logger.error("配置文件不存在: {}".format(config_file))
            logger.error("已在以下位置查找：当前目录、脚本目录、backend目录、项目根目录")
            sys.exit(1)

        logger.info("使用配置文件: {}".format(truncate_path(config_path, PROJECT_ROOT)))

        config = configparser.ConfigParser()
        config.read(config_path)

        # 检查必要的配置项
        required_sections = ["mysql", "sql"]
        required_mysql_options = ["host", "user", "password", "database"]
        required_sql_options = ["directory"]

        for section in required_sections:
            if not config.has_section(section):
                logger.error("配置文件缺少必要的section: {}".format(section))
                sys.exit(1)

        # 检查mysql配置项
        for option in required_mysql_options:
            if not config.has_option("mysql", option):
                logger.error("mysql配置缺少必要的选项: {}".format(option))
                sys.exit(1)

        # 检查sql配置项
        for option in required_sql_options:
            if not config.has_option("sql", option):
                logger.error("sql配置缺少必要的选项: {}".format(option))
                sys.exit(1)

        host = config.get("mysql", "host")
        user = config.get("mysql", "user")
        password = config.get("mysql", "password")
        database = config.get("mysql", "database")

        if config_env == 'test':
            logger.info("使用测试环境密码")
            password = 'xxxx@'
        else:
            logger.info("使用配置文件中的密码")

        # 获取SQL目录，支持相对路径和绝对路径
        sql_dir = config.get("sql", "directory")
        if not os.path.isabs(sql_dir):
            # 如果是相对路径，则相对于配置文件所在目录
            config_dir = os.path.dirname(config_path)
            sql_dir = os.path.abspath(os.path.join(config_dir, sql_dir))

        if not os.path.exists(sql_dir):
            logger.error("SQL目录不存在: {}".format(truncate_path(sql_dir, PROJECT_ROOT)))
            sys.exit(1)

        # 获取SQL文件列表
        sql_file_paths = []
        for root, dirs, files in os.walk(sql_dir):
            sql_files = [f for f in files if f.endswith('.sql')]
            for sql_file in sql_files:
                sql_file_paths.append(os.path.join(root, sql_file))

        # 处理init目录
        init_dir = os.path.join(sql_dir, 'init')
        init_file_paths = []
        if os.path.exists(init_dir):
            init_files = [f for f in os.listdir(init_dir) if f.endswith('.sql')]
            init_files.sort(key=lambda f: int(f.split('.')[0]) if f.split('.')[0].isdigit() else f)
            init_file_paths = [os.path.join(init_dir, f) for f in init_files]

        sql_file_paths = init_file_paths + [f for f in sql_file_paths if f not in init_file_paths]

        return host, user, password, database, sql_file_paths

    except Exception as e:
        logger.error("读取配置文件时发生错误: {}".format(str(e)))
        sys.exit(1)

def ensure_database_exists(host, user, password, database):
    logger.info("检查数据库 {} 是否存在".format(database))

    try:
        # 首先尝试创建数据库连接
        connection = mysql.connector.connect(
            host=host,
            user=user,
            password=password
        )
        cursor = connection.cursor()

        try:
            # 检查数据库是否存在
            cursor.execute("SELECT SCHEMA_NAME FROM INFORMATION_SCHEMA.SCHEMATA WHERE SCHEMA_NAME = '{}'".format(database))
            result = cursor.fetchone()

            if not result:
                logger.info("数据库 {} 不存在，开始创建...".format(database))
                cursor.execute("CREATE DATABASE IF NOT EXISTS {} CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci".format(database))
                logger.info("数据库 {} 创建成功！".format(database))
            else:
                logger.info("数据库 {} 已存在".format(database))

        except mysql.connector.Error as err:
            logger.error("操作数据库时出错: {}".format(err))
            sys.exit(1)
        finally:
            cursor.close()
            connection.close()

    except mysql.connector.Error as err:
        if err.errno == mysql.connector.errorcode.ER_ACCESS_DENIED_ERROR:
            logger.error("数据库访问被拒绝：用户名或密码错误")
        elif err.errno == mysql.connector.errorcode.CR_CONN_HOST_ERROR:
            logger.error("无法连接到数据库服务器，请检查服务器是否运行或网络连接")
        else:
            logger.error("连接数据库时出错: {}".format(err))
        sys.exit(1)

def check_sql_mode(host, user, password):
    """检查MySQL的sql_mode设置"""
    try:
        connection = mysql.connector.connect(
            host=host,
            user=user,
            password=password
        )
        cursor = connection.cursor()

        try:
            # 获取当前的sql_mode
            cursor.execute("SELECT @@sql_mode;")
            current_mode = cursor.fetchone()[0]
            logger.info("当前SQL MODE: %s", current_mode)

            # 检查是否包含ONLY_FULL_GROUP_BY
            if 'ONLY_FULL_GROUP_BY' in current_mode:
                logger.warning("SQL MODE 包含 ONLY_FULL_GROUP_BY，这可能会导致某些GROUP BY查询失败")
                logger.info("建议的SQL MODE: STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION")

                # 询问是否要修改sql_mode
                while True:
                    response = input("是否要移除 ONLY_FULL_GROUP_BY 模式？(y/n): ").lower()
                    if response in ['y', 'n']:
                        break
                    print("请输入 y 或 n")

                if response == 'y':
                    # 构建新的sql_mode
                    new_mode = current_mode.replace(',ONLY_FULL_GROUP_BY', '').replace('ONLY_FULL_GROUP_BY,', '')
                    if new_mode == current_mode:  # 如果是单独的ONLY_FULL_GROUP_BY
                        new_mode = new_mode.replace('ONLY_FULL_GROUP_BY', '')

                    # 设置新的sql_mode
                    cursor.execute("SET GLOBAL sql_mode = %s;", (new_mode,))
                    logger.info("SQL MODE 已更新为: %s", new_mode)

                    # 提示需要重启MySQL服务
                    logger.info("请注意：要使更改永久生效，需要修改MySQL配置文件并重启服务")
                    logger.info("配置文件路径通常在: /etc/my.cnf 或 /opt/homebrew/etc/my.cnf")
                    logger.info("添加或修改以下配置：")
                    logger.info("[mysqld]")
                    logger.info("sql_mode=%s", new_mode)
            else:
                logger.info("SQL MODE 配置正常，不包含 ONLY_FULL_GROUP_BY")

        finally:
            cursor.close()
            connection.close()

    except mysql.connector.Error as err:
        if err.errno == mysql.connector.errorcode.ER_ACCESS_DENIED_ERROR:
            logger.error("检查SQL MODE失败：用户名或密码错误")
        elif err.errno == mysql.connector.errorcode.CR_CONN_HOST_ERROR:
            logger.error("检查SQL MODE失败：无法连接到数据库服务器")
        else:
            logger.error("检查SQL MODE失败：%s", str(err))
        return False
    except Exception as e:
        logger.error("检查SQL MODE时发生未知错误：%s", str(e))
        return False

    return True

if __name__ == "__main__":
    try:
        # 首先检查MySQL客户端工具是否已安装
        check_mysql_client_installed()

        parser = argparse.ArgumentParser()
        parser.add_argument('--config', help='Configuration environment (e.g. test)')
        args = parser.parse_args()

        config_file = "config.ini"

        # 读取配置
        host, user, password, database, sql_file_paths = read_config(config_file, args.config)

        # 测试数据库连接
        if not test_mysql_connection(host, user, password):
            logger.error("数据库连接测试失败，程序终止")
            sys.exit(1)

        # 检查SQL MODE（作为必要步骤）
        logger.info("检查MySQL SQL MODE配置...")
        if not check_sql_mode(host, user, password):
            logger.error("检查SQL MODE失败，程序终止")
            sys.exit(1)

        # 确保数据库存在
        ensure_database_exists(host, user, password, database)

        # 执行SQL脚本
        execute_sql_scripts_with_source(sql_file_paths, host, user, password, database)

        logger.info("所有操作已成功完成！")

    except KeyboardInterrupt:
        logger.error("程序被用户中断")
        sys.exit(1)
    except Exception as e:
        logger.error("程序执行过程中发生未知错误: {}".format(str(e)))
        sys.exit(1)
